Das Ziel ist es, aus dem Datacamp Datensatz Video Game Sales Date folgende Fragestellung / Hypothese zu beantworten:

Fragestellung: “Ist es wahrscheinlicher, dass sich bestimmte Spielgenres in Europa signifikant (Unterschied von 50%) besser verkaufen lassen als im Japanischen und Nordamerikanischen Markt?”

Als Einführung werden wir auf Datacamp folgende Kurse durchgehen:

# Import libraries
library("plotly")
library("ggplot2")
library("plyr")
library("dplyr")
library("broom")
library("gridExtra")   
view(df)
Error in view(df) : could not find function "view"

Data Wrangling

Bevor wir mit den Visualisierungen und dem Erstellen der Modelle beginnen können, müssen wir die Daten säubern. Das heisst es sollte keine Duplikate geben, fehlende Werte sollten korrekt eingetragen werden und Daten, die nicht verwendet werden, sollten gelöscht werden.

Es hat “N/A” Werte in den Spalten “Year” und “Publisher”. Diese Werte sollten korrekte “NA” Werte sein, damit sie bei den Visualisierungen und Berechnungen nicht berücksichtigt werden.

# Show rows with "N/A" values
df[grep("N/A", df$Publisher),]
df[grep("N/A", df$Year),]
# Replace "N/A" with "NA"
df[df == "N/A"] <- NA

df <- df %>%
  filter(df$Global_Sales > 0.1)

df
# Check if values have been converted
df %>% 
  summarize(across(everything(), ~sum(is.na(.))))

Da 2017 nur 3 Einträge und 2020 nur 1 Eintrag beinhaltet, werden wir diese Jahren nicht berücksichtigen und aus dem Dataframe löschen. Da diese nicht vollständig sind, könnten unsere Modelle ungenau werden.

# Remove years 2017 and 2020 from dataset
df_clean <- df[!(df$Year == "2017" | df$Year == "2020"),]

# Remove unnecessary columns because they are not needed for our thesis
drop <- c("Rank")
df_clean <- df_clean[,!(names(df_clean) %in% drop)]

df_clean
# Set data to correct type
df_clean$Genre <- as.factor(df_clean$Genre)
df_clean$Year <- as.numeric(df_clean$Year)

Erste Plots erstellen

Wir erstellen ein paar erste Plots um uns einen Überblick über die Daten zu verschaffen.

na <- sum(df_clean[, 'NA_Sales'], na.rm = TRUE)
eu <- sum(df_clean[, 'EU_Sales'], na.rm = TRUE)
jp <- sum(df_clean[, 'JP_Sales'], na.rm = TRUE)
o <- sum(df_clean[, 'Other_Sales'], na.rm = TRUE)
g <- sum(df_clean[, 'Global_Sales'], na.rm = TRUE)

fig <- plot_ly(
  y = c(na, eu, jp, o), 
  x = c("North America", "Europe", "Japan", "Other"), 
  type = 'bar',
  width = 800
)

fig <- fig %>% layout(title = "Video Game Sales Overview",
         xaxis = list(title = "Region"),
         yaxis = list(title = "Sales (million)"))

fig

Anhand dieses Diagrammes, kann man sehen, dass Nordamerika mit Abstand der grösste Absatzmarkt im Game-Bereich ist.

Nun wollen wir noch die Verteilung der verschiedenen Spielgenres besser sehen:

# Group by genre and summarize game sales to each region
df_genre <- df_clean %>%
  group_by(Genre) %>%
  summarize(
    NA_Sales_Sum = sum(NA_Sales),
    EU_Sales_Sum = sum(EU_Sales), 
    JP_Sales_Sum = sum(JP_Sales),
    Other_Sales_Sum = sum(Other_Sales),
    Global_Sales_Sum = sum(Global_Sales)
  )

# Plot grouped bar chart video game sales by genre
fig <- plot_ly(
  df_genre, y = ~Genre, x = ~NA_Sales_Sum, type = "bar", name = "North America", width = 1000, height = 800) %>% 
  add_trace(x = ~EU_Sales_Sum, name = "Europe") %>%
  add_trace(x = ~JP_Sales_Sum, name = "Japan") %>%
  add_trace(x = ~Other_Sales_Sum, name = "Other") %>%
  layout(
    title = "Video Game Sales by Genre",
    xaxis = list(title = "Sales (million)"),
    barmode = "group"
  )

fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations

Hier sieht man gut, dass Japan im Vergleich zu den anderen Märkten viel mehr Role-Play und Strategie Spiele verkauft. Dafür werden in Japan aber viel weniger Shooter und Action Spiele als den anderen Regionen verkauft.

df_year <- df_clean %>%
  group_by(Year) %>%
  summarize(
    NA_Sales_Sum = sum(NA_Sales),
    EU_Sales_Sum = sum(EU_Sales), 
    JP_Sales_Sum = sum(JP_Sales),
    Other_Sales_Sum = sum(Other_Sales),
    Global_Sales_Sum = sum(Global_Sales)
  )

fig <- plot_ly(
  df_year, y = ~NA_Sales_Sum, x = ~Year, type = "bar", name = "North America", width = 900, height = 500) %>% 
  add_trace(y = ~EU_Sales_Sum, name = "Europe") %>%
  add_trace(y = ~JP_Sales_Sum, name = "Japan") %>%
  add_trace(y = ~Other_Sales_Sum, name = "Other") %>%
  layout(
    title = "Video Game from Sales by Year",
    xaxis = list(title = "Year"),
    yaxis = list(title = "Sales (million)"),
    barmode = "stack"
  )

fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations

Überraschenderweise, sieht man in diesem Diagramm, dass die Videospiel Verkäufe zwischen den Jahren 2005 und 2012 geboomt haben. Danach ist die Kurve wieder abgeflacht. Dies könnte unter anderem daran liegen, dass in dieser Zeit viele neue Konsolen auf den Markt gekommen sind, welche grosse Neuerungen mit sich brachten. Die Playstation 3, die Xbox 360 und die Nintendo Wii sind alle in 2005-2006 auf den Markt gekommen und haben den Spielemarkt anscheinend stark beeinflusst.

# Calculate the average of sales of each genre from each region
df_sales_avg <- df_clean %>%
  group_by(Genre) %>%
  summarise(
    EU_Sales_Avg = mean(EU_Sales),
    NA_Sales_Avg = mean(NA_Sales),
    JP_Sales_Avg = mean(JP_Sales),
    Other_Sales_Avg = mean(Other_Sales),
    Global_Sales_Avg = mean(Global_Sales))

df_sales_avg

Hier sehen wir die durchschnittliche Anzhal von Verkäufe pro Genre.

Regressionsmodelle

Da wir unsere Daten jetzt besser verstehen, können wir mit den Regressionsmodellen und mit der Beantwortung unserer Fragestellung beginnen. Allerdings mussten wir feststellen, dass unsere Fragestellung ungeschickt war und wir diese nicht ausreichend beantworten können. Daher haben wir uns folgende Alternativ-Fragestellung überlegt:

Kann man anhand der nordamerikanischen Verkäufe voraussagen, wie sich ein Genre im europäischen Markt verkaufen wird?

Diese Frage könnte einer Firma dabei helfen zu entscheiden, wieviel Werbebudget diese in Europa ausgeben soll, nachdem ein Spiel in Amerika bereits auf den Markt gekommen ist.

Daten optimieren

In unserem Datensatz gibt es einige Ausreisser. Zum Beispiel gibt es Spiele, welche nur in einzelnen Regionen auf den Markt gekommen sind und die in anderen Ländern garnicht verkauft wurden. Diese Tatsache würde beim Erstellen der Modelle zu grossen Abweichungen und Ungenauigkeiten führen. Daher berücksichtigen wir nur die Games, welche über 1 Mio. Verkäufe haben.

Unten haben wir die Genres mit den besten Fits genommen, da bei den anderen Spielgenres der r-squared Wert unter 50% lag. Das bedeutet, dass es dort keinen Zusammenhang gibt, weshalb wir diese ebenfalls nicht beachten werden.

Wir erstellen das Dataframe mit allen Racing Games:

# Create DataFrame only with Racing games
df_racing <- df_clean %>%
  filter(
    Genre == "Racing",
    NA_Sales > 1.00,
    EU_Sales > 1.00
  )

#create scatterplot
ggplot(df_racing, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Racing Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_racing <- lm(EU_Sales ~ NA_Sales, data = df_racing)

# Extract model score
mdl_racing %>%
  glance() %>%
  pull(r.squared)
[1] 0.8686435
# Predict EU Sales for a Racing Game based on NA Sales
predict_racing <- tibble(NA_Sales = 5)
predict(mdl_racing, predict_racing)
       1 
3.891789 

Wenn ein Racing Game in Nord Amerika 5 Millionen Verkäufe aufweist, liegt die Verkaufs-Vorhersage für Europa bei rund 3.9 Millionen. In Amerika sind diese Spiele also beliebter als bei uns in Europa.

# Create DataFrame only with Role-Playing games
df_role <- df_clean %>%
  filter(
    Genre == "Role-Playing",
    NA_Sales > 1.00,
    EU_Sales > 1.00
  )

#create scatterplot
ggplot(df_role, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Role-Playing Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_rp <- lm(EU_Sales ~ NA_Sales, data = df_role)

# Extract model score
mdl_rp %>%
  glance() %>%
  pull(r.squared)
[1] 0.8433709
# Predict EU Sales for a Role-Playing Game based on NA Sales
predict_rp <- tibble(NA_Sales = 5)
predict(mdl_rp, predict_rp)
       1 
3.451286 
# Create DataFrame only with Shooter games
df_shooter <- df_clean %>%
  filter(
    Genre == "Shooter",
    NA_Sales > 0.00,
    EU_Sales > 0.00
  )

# Create DataFrame only with Shooter games without Duck Hunt
df_shooter_no_duckhunt <- df_clean %>%
  filter(
    Genre == "Shooter",
    NA_Sales > 0.00,
    Name != "Duck Hunt",
    EU_Sales > 0.00
  )

# Create subplots
p1 <- ggplot(df_shooter, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Shooter Game Sales")

p2 <- ggplot(df_shooter_no_duckhunt, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Shooter Game Sales no Duck Hunt")

# Plot both plots side by side
grid.arrange(p1, p2, ncol = 2)  
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

Dies ist ein Vergleich der nordamerikanischen und der europäischen Verkäufen von Shooter-Spielen. Auf dem linken Plot ist ein ganz gewaltiger Ausreisser zu sehen, ganz oben links im Bild. Das ist das Spiel “Duck Hunt”. Wir haben mehr über dieses Spiel recherchiert und herausgefunden, dass dieses Spiel zum Release des Nintendo Entertainment System kurz NES herausgekommen ist. Beim Kauf einer NES gab es das Spiel meistens dazu. Zu dem Zeitpunkt, kam die NES aber erst in Amerika auf den Markt und noch nicht in Europa. Daher sieht man, dass die Verkäufe in Amerika fast bei 30 Mio. lag und in europa sogar weniger als 1 Mio. Einheiten. Im Plot rechts, haben wir diesen Ausreiser entfernt und wir sehen direkt ein viel genaueres Bild.

# Create linear model
mdl_shooter <- lm(EU_Sales ~ NA_Sales, data = df_shooter)

# Extract model score
mdl_shooter %>%
  glance() %>%
  pull(r.squared)
[1] 0.4244135
# Create linear model
mdl_shooter_no_duckhunt <- lm(EU_Sales ~ NA_Sales, data = df_shooter_no_duckhunt)

# Extract model score
mdl_shooter_no_duckhunt %>%
  glance() %>%
  pull(r.squared)
[1] 0.6858556

Wie wir hier anhand der R-Squared sehen, fitted das Modell viel besser ohne Duck Hunt.

Aber da das Modell auch ohne Duck Hunt nicht sehr gut ist, werden wir mit diesem Genre keine Vorhersagen durchführen. Diese währen zu ungenau und wir könnten nicht dahinter stehen.

# Create DataFrame only with Simulation games
df_sim <- df_clean %>%
  filter(
    Genre == "Simulation",
    NA_Sales > 1.00,
    EU_Sales > 1.00
)

ggplot(df_sim, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Simulation Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

Auf diesem Plot gibt es wieder einen Punkt, der weit weg von den anderen ist. Dies ist jedoch in diesem Fall kein Ausreiser, da hier die Verkäufe von Nordamerika und Europa sehr ähnlich sind. Bei diesem extrem beliebten Spiel handelt es sich übrigens um den Hundesimulator “Nintendogs”. ;-)

# Create linear model
mdl_sim <- lm(EU_Sales ~ NA_Sales, data = df_sim)

# Extract model score
mdl_sim %>%
  glance() %>%
  pull(r.squared)
[1] 0.9233418
# Predict EU Sales for a Simulation Game based on NA Sales
predict_sim <- tibble(NA_Sales = 5)
predict(mdl_sim, predict_sim)
       1 
5.474846 

Wenn ein Simulation Game in NA 5 Million Verkäufe aufweist, liegt die Vorhersage für EU bei 5.4 Millionen.

# Create DataFrame only with Sport games
df_sport <- df_clean %>%
  filter(
    Genre == "Sports",
    NA_Sales > 1.00,
    EU_Sales > 1.00
  )

ggplot(df_sport, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Sport Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

Hier haben wir wieder ein Spiel, welches sich von den Massen abhebt, aber in Amerika sowie in Europa extrem beliebt war. Es handelt sich natürlich um das Kultspiel “Wii Sports”. Dieses wurde einem auch meistens beim Kauf einer Nintendo Wii Konsole dazu geschenkt.

# Create linear model
mdl_sport <- lm(EU_Sales ~ NA_Sales, data = df_sport)

# Extract model score
mdl_sport %>%
  glance() %>%
  pull(r.squared)
[1] 0.9488926
# Predict EU Sales for a Sport Game based on NA Sales
predict_sport <- tibble(NA_Sales = 5)
predict(mdl_sport, predict_sport)
       1 
4.019965 

Wenn ein Sport Game in NA 5 Million Verkäufe aufweist, liegt die Vorhersage für EU bei 4 Millionen.

Residuenanalyse (zum beurteilen ob das Modell gut ist)

Die Residuen sollten folgende Punkte erfüllen:

  • Residuen haben den erwartungswert von 0
  • Residuen sind voneinander unabhängig
  • Residuen sind normalverteilt

Dies müssen wir nun noch prüfen, um zu bestimmen, ob unsere Vorhersagen verlässlich sind.

# Create Residual Scatterplot

df <- augment(mdl_racing)

ggplot(df, aes(x = 1:nrow(df), y = .resid)) + 
  geom_point() +
  geom_hline(yintercept=0, color="Red") +
  ggtitle("Residuals Model Racing Genre") +
  xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)

ggplot(df_racing, aes(x = mdl_racing$residuals)) + 
  geom_histogram(bins = 30) +
  geom_density(color = "Red") +
  ggtitle("Residuals Model Racing Genre") +
  xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das so akzeptieren können.

# Create Residual Scatterplot

df <- augment(mdl_rp)

ggplot(df, aes(x = 1:nrow(df), y = .resid)) + 
  geom_point() +
  geom_hline(yintercept=0, color="Red") +
  ggtitle("Residuals Model Role-Playing Genre") +
  xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)

ggplot(df_role, aes(x = mdl_rp$residuals)) + 
  geom_histogram(bins = 30) +
  geom_density(color = "Red") +
  ggtitle("Residuals Model Role-Playing Genre") +
  xlab("residuals")

Auch sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das so akzeptieren können.

# Create Residual Scatterplot

df <- augment(mdl_sim)

ggplot(df, aes(x = 1:nrow(df), y = .resid)) + 
  geom_point() +
  geom_hline(yintercept=0, color="Red") +
  ggtitle("Residuals Model Simulation Genre") +
  xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)

ggplot(df_sim, aes(x = mdl_sim$residuals)) + 
  geom_histogram(bins = 30) +
  geom_density(color = "Red") +
  ggtitle("Residuals Model Simulation Genre") +
  xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das so akzeptieren können.

# Create Residual Scatterplot

df <- augment(mdl_sport)

ggplot(df, aes(x = 1:nrow(df), y = .resid)) + 
  geom_point() +
  geom_hline(yintercept=0, color="Red") +
  ggtitle("Residuals Model Sport Genre") +
  xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)

ggplot(df_sport, aes(x = mdl_sport$residuals)) + 
  geom_histogram(bins = 50) +
  geom_density(color = "Red") +
  ggtitle("Residuals Model Sport Genre") +
  xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das so akzeptieren können.

Wie ähnlich sind sich die Verkaufszahlen von Nordamerika und Europa? Und wie verhalten sich die japanischen Verkaufszahlen im Vergleich zu den zwei westlichen Absatzmärkten?

# Color the SPLOM of NA_Sales, EU_Sales, and JP_Sales by nintendo
df_clean %>%
  plot_ly(color = ~Genre) %>% 
  add_trace(
    type = 'splom',
    dimensions = list(
      list(label = 'N. America', values = ~NA_Sales),
      list(label = 'Europe', values = ~EU_Sales),    
      list(label = 'Japan', values = ~JP_Sales)       
    )
  )
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Äusserst spannend zu sehen ist, dass sich grosse Unterschiede sehen lassen, wie sich bestimmte Genre verkauft haben. Die geographische Lage von dem Absatzmarkt ist dabei eher zweitrangig. Mit Abstand am häufigsten wurden Action Spiele verkauft.

Verkaufen sich Nintendo Spiele weltweit besser als Spiele Electronic Arts.

Dafür müssen wir zuerst unser Dataframe Filtern, damit wir nurnoch Spiele von Nintendo und Electronic Arts haben.

# Create DataFrame with only Nintendo and Sony as Publisher
publishers = c("Nintendo", "Electronic Arts")

df_publisher <- df_clean %>%
  filter(
    Publisher %in% publishers
    )

df_publisher
# Replace publisher name with 0 and 1
df_publisher$Publisher[df_publisher$Publisher == "Electronic Arts"] <- 0
df_publisher$Publisher[df_publisher$Publisher == "Nintendo"] <- 1

# Save as int
df_publisher$Publisher <- as.numeric(df_publisher$Publisher)
# Create logistic model
mdl_publisher <- glm(Publisher ~ NA_Sales, data = df_publisher, family = binomial())
ggplot(df_publisher, aes(x=Global_Sales, y=Publisher)) + 
  geom_point(alpha=.5, color="Blue") +
  stat_smooth(method="glm", col = "Red", se=FALSE, method.args = list(family=binomial)) +
  labs(
    x = "Sales (million)",
    y = "1=Nintendo / 0=Electronic Arts",
    title = "Probability that a game is from Nintendo based on global sales"
  )
`geom_smooth()` using formula 'y ~ x'

Wir können hier gut sehen, dass Nintendo viel erfolgreichere Spiele produziert hat. Dies deutet auch an, dass Nintendo beliebter ist als Electronic Arts.

Verkaufen sich in Japan Nintendo Wii Spiele besser als Nintendo DS Spiele?

Nun filtern wir zuerst unseren Dataframe nach Wii und nach DS Spielen.

# Create DataFrame with only Wii and DS as Platforms
platforms = c("Wii", "DS")

df_platform <- df_clean %>%
  filter(
    Platform %in% platforms
    )

df_platform
# Replace platform name with 0 and 1
df_platform$Platform[df_platform$Platform == "Wii"] <- 0
df_platform$Platform[df_platform$Platform == "DS"] <- 1

# Save as int
df_platform$Platform <- as.numeric(df_platform$Platform)
# Create logistic model
mdl_platform <- glm(Platform ~ JP_Sales, data = df_platform, family = binomial())
ggplot(df_platform, aes(x=JP_Sales, y=Platform)) + 
  geom_point(alpha=.2, color="Blue") +
  stat_smooth(method="glm", col = "Red", se=FALSE, method.args = list(family=binomial)) +
  labs(
    x = "Sales (million)",
    y = "1=Wii / 0=DS",
    title = "Probability that a game is from Wii based on sales in Japan"
  )
`geom_smooth()` using formula 'y ~ x'

Anhand von diesem Plot kann man erkennen, dass ein Spiel, welches sich über 2 Mio. mal verkauft hat, eher über die Platform Wii verkauft wurde. Daraus könnte man deuten, dass die Nintendo Wii in Japan beliebter ist als der Nintendo DS.

Fazit

Bei den meisten Genren ist es nicht möglich die Verkäufe in Europa anhand der Verkäufe in Nord Amerika vorherzusagen. Wir haben jedoch einige Genren gefunden, bei denen Vorhersagen möglich ist:

  • Racing / Rennspiele
  • Role-Playing / Rollenspiele
  • Simulation / Simulationsspiele
  • Sport / Sportspiele

Sehr interessant zu sehen war das Genre Simulation. Unser Modell, welches bei 92% Genauigkeit liegt sagt voraus, dass ein beliebiges Spiel in Europa besser verkauft wird als in Nord Amerika. Bei allen anderen Genren verkaufen sich die Spiele in Nord Amerika besser.

LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBtb2RlbHMgd2l0aCBSIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpEYXMgWmllbCBpc3QgZXMsIGF1cyBkZW0gRGF0YWNhbXAgRGF0ZW5zYXR6IFtWaWRlbyBHYW1lIFNhbGVzIERhdGVdKGh0dHBzOi8vYXBwLmRhdGFjYW1wLmNvbS93b3Jrc3BhY2UvZGF0YXNldHMvZGF0YXNldC1weXRob24tdmlkZW8tZ2FtZXMtc2FsZXMpIGZvbGdlbmRlIEZyYWdlc3RlbGx1bmcgLyBIeXBvdGhlc2UgenUgYmVhbnR3b3J0ZW46CgoKIyMjIEZyYWdlc3RlbGx1bmc6ICJJc3QgZXMgd2FocnNjaGVpbmxpY2hlciwgZGFzcyBzaWNoIGJlc3RpbW10ZSBTcGllbGdlbnJlcyBpbiBFdXJvcGEgc2lnbmlmaWthbnQgKFVudGVyc2NoaWVkIHZvbiA1MCUpIGJlc3NlciB2ZXJrYXVmZW4gbGFzc2VuIGFscyBpbSBKYXBhbmlzY2hlbiB1bmQgTm9yZGFtZXJpa2FuaXNjaGVuIE1hcmt0PyIKCgpBbHMgRWluZsO8aHJ1bmcgd2VyZGVuIHdpciBhdWYgRGF0YWNhbXAgZm9sZ2VuZGUgS3Vyc2UgZHVyY2hnZWhlbjoKCi0gW0ludHJvZHVjdGlvbiB0byBSZWdyZXNzaW9uIGluIFJdKGh0dHBzOi8vYXBwLmRhdGFjYW1wLmNvbS9sZWFybi9jb3Vyc2VzL2ludHJvZHVjdGlvbi10by1yZWdyZXNzaW9uLWluLXIpCgotIFtJbnRlcm1lZGlhdGUgUmVncmVzc2lvbiBpbiBSXShodHRwczovL2FwcC5kYXRhY2FtcC5jb20vbGVhcm4vY291cnNlcy9pbnRlcm1lZGlhdGUtcmVncmVzc2lvbi1pbi1yKQoKCmBgYHtyfQojIEltcG9ydCBsaWJyYXJpZXMKbGlicmFyeSgicGxvdGx5IikKbGlicmFyeSgiZ2dwbG90MiIpCmxpYnJhcnkoInBseXIiKQpsaWJyYXJ5KCJkcGx5ciIpCmxpYnJhcnkoImJyb29tIikKbGlicmFyeSgiZ3JpZEV4dHJhIikgICAKYGBgCgpgYGB7cn0KIyBSZWFkIGNzdiBmcm9tIGZvbGRlciAiZGF0YSIKZGYgPSByZWFkLmNzdigiLi9kYXRhL3ZpZGVvX2dhbWVzX2RhdGEuY3N2IikKCmhlYWQoZGYsIDEwKQpgYGAKIyMjIERhdGEgV3JhbmdsaW5nCkJldm9yIHdpciBtaXQgZGVuIFZpc3VhbGlzaWVydW5nZW4gdW5kIGRlbSBFcnN0ZWxsZW4gZGVyIE1vZGVsbGUgYmVnaW5uZW4ga8O2bm5lbiwgbcO8c3NlbiB3aXIgZGllIERhdGVuIHPDpHViZXJuLiBEYXMgaGVpc3N0IGVzIHNvbGx0ZSBrZWluZSBEdXBsaWthdGUgZ2ViZW4sIGZlaGxlbmRlIFdlcnRlIHNvbGx0ZW4ga29ycmVrdCBlaW5nZXRyYWdlbiB3ZXJkZW4gdW5kIERhdGVuLCBkaWUgbmljaHQgdmVyd2VuZGV0IHdlcmRlbiwgc29sbHRlbiBnZWzDtnNjaHQgd2VyZGVuLgoKRXMgaGF0ICJOL0EiIFdlcnRlIGluIGRlbiBTcGFsdGVuICJZZWFyIiB1bmQgIlB1Ymxpc2hlciIuIERpZXNlIFdlcnRlIHNvbGx0ZW4ga29ycmVrdGUgIk5BIiBXZXJ0ZSBzZWluLCBkYW1pdCBzaWUgYmVpIGRlbiBWaXN1YWxpc2llcnVuZ2VuIHVuZCBCZXJlY2hudW5nZW4gbmljaHQgYmVyw7xja3NpY2h0aWd0IHdlcmRlbi4KCmBgYHtyfQojIFNob3cgcm93cyB3aXRoICJOL0EiIHZhbHVlcwpkZltncmVwKCJOL0EiLCBkZiRQdWJsaXNoZXIpLF0KZGZbZ3JlcCgiTi9BIiwgZGYkWWVhciksXQpgYGAKCmBgYHtyfQojIFJlcGxhY2UgIk4vQSIgd2l0aCAiTkEiCmRmW2RmID09ICJOL0EiXSA8LSBOQQoKZGYgPC0gZGYgJT4lCiAgZmlsdGVyKGRmJEdsb2JhbF9TYWxlcyA+IDAuMSkKCmRmCmBgYAoKYGBge3J9CiMgQ2hlY2sgaWYgdmFsdWVzIGhhdmUgYmVlbiBjb252ZXJ0ZWQKZGYgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+c3VtKGlzLm5hKC4pKSkpCmBgYApEYSAyMDE3IG51ciAzIEVpbnRyw6RnZSB1bmQgMjAyMCBudXIgMSBFaW50cmFnIGJlaW5oYWx0ZXQsIHdlcmRlbiB3aXIgZGllc2UgSmFocmVuIG5pY2h0IGJlcsO8Y2tzaWNodGlnZW4gdW5kIGF1cyBkZW0gRGF0YWZyYW1lIGzDtnNjaGVuLiBEYSBkaWVzZSBuaWNodCB2b2xsc3TDpG5kaWcgc2luZCwga8O2bm50ZW4gdW5zZXJlIE1vZGVsbGUgdW5nZW5hdSB3ZXJkZW4uCgpgYGB7cn0KIyBSZW1vdmUgeWVhcnMgMjAxNyBhbmQgMjAyMCBmcm9tIGRhdGFzZXQKZGZfY2xlYW4gPC0gZGZbIShkZiRZZWFyID09ICIyMDE3IiB8IGRmJFllYXIgPT0gIjIwMjAiKSxdCgojIFJlbW92ZSB1bm5lY2Vzc2FyeSBjb2x1bW5zIGJlY2F1c2UgdGhleSBhcmUgbm90IG5lZWRlZCBmb3Igb3VyIHRoZXNpcwpkcm9wIDwtIGMoIlJhbmsiKQpkZl9jbGVhbiA8LSBkZl9jbGVhblssIShuYW1lcyhkZl9jbGVhbikgJWluJSBkcm9wKV0KCmRmX2NsZWFuCmBgYAoKYGBge3J9CiMgU2V0IGRhdGEgdG8gY29ycmVjdCB0eXBlCmRmX2NsZWFuJEdlbnJlIDwtIGFzLmZhY3RvcihkZl9jbGVhbiRHZW5yZSkKZGZfY2xlYW4kWWVhciA8LSBhcy5udW1lcmljKGRmX2NsZWFuJFllYXIpCmBgYAoKIyMjIEVyc3RlIFBsb3RzIGVyc3RlbGxlbgoKV2lyIGVyc3RlbGxlbiBlaW4gcGFhciBlcnN0ZSBQbG90cyB1bSB1bnMgZWluZW4gw5xiZXJibGljayDDvGJlciBkaWUgRGF0ZW4genUgdmVyc2NoYWZmZW4uCgpgYGB7cn0KbmEgPC0gc3VtKGRmX2NsZWFuWywgJ05BX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkKZXUgPC0gc3VtKGRmX2NsZWFuWywgJ0VVX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkKanAgPC0gc3VtKGRmX2NsZWFuWywgJ0pQX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkKbyA8LSBzdW0oZGZfY2xlYW5bLCAnT3RoZXJfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQpnIDwtIHN1bShkZl9jbGVhblssICdHbG9iYWxfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQoKZmlnIDwtIHBsb3RfbHkoCiAgeSA9IGMobmEsIGV1LCBqcCwgbyksIAogIHggPSBjKCJOb3J0aCBBbWVyaWNhIiwgIkV1cm9wZSIsICJKYXBhbiIsICJPdGhlciIpLCAKICB0eXBlID0gJ2JhcicsCiAgd2lkdGggPSA4MDAKKQoKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIlZpZGVvIEdhbWUgU2FsZXMgT3ZlcnZpZXciLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiUmVnaW9uIiksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJTYWxlcyAobWlsbGlvbikiKSkKCmZpZwpgYGAKQW5oYW5kIGRpZXNlcyBEaWFncmFtbWVzLCBrYW5uIG1hbiBzZWhlbiwgZGFzcyBOb3JkYW1lcmlrYSBtaXQgQWJzdGFuZCBkZXIgZ3LDtnNzdGUgQWJzYXR6bWFya3QgaW0gR2FtZS1CZXJlaWNoIGlzdC4gCgoKTnVuIHdvbGxlbiB3aXIgbm9jaCBkaWUgVmVydGVpbHVuZyBkZXIgdmVyc2NoaWVkZW5lbiBTcGllbGdlbnJlcyBiZXNzZXIgc2VoZW46CgpgYGB7cn0KIyBHcm91cCBieSBnZW5yZSBhbmQgc3VtbWFyaXplIGdhbWUgc2FsZXMgdG8gZWFjaCByZWdpb24KZGZfZ2VucmUgPC0gZGZfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoR2VucmUpICU+JQogIHN1bW1hcml6ZSgKICAgIE5BX1NhbGVzX1N1bSA9IHN1bShOQV9TYWxlcyksCiAgICBFVV9TYWxlc19TdW0gPSBzdW0oRVVfU2FsZXMpLCAKICAgIEpQX1NhbGVzX1N1bSA9IHN1bShKUF9TYWxlcyksCiAgICBPdGhlcl9TYWxlc19TdW0gPSBzdW0oT3RoZXJfU2FsZXMpLAogICAgR2xvYmFsX1NhbGVzX1N1bSA9IHN1bShHbG9iYWxfU2FsZXMpCiAgKQoKIyBQbG90IGdyb3VwZWQgYmFyIGNoYXJ0IHZpZGVvIGdhbWUgc2FsZXMgYnkgZ2VucmUKZmlnIDwtIHBsb3RfbHkoCiAgZGZfZ2VucmUsIHkgPSB+R2VucmUsIHggPSB+TkFfU2FsZXNfU3VtLCB0eXBlID0gImJhciIsIG5hbWUgPSAiTm9ydGggQW1lcmljYSIsIHdpZHRoID0gMTAwMCwgaGVpZ2h0ID0gODAwKSAlPiUgCiAgYWRkX3RyYWNlKHggPSB+RVVfU2FsZXNfU3VtLCBuYW1lID0gIkV1cm9wZSIpICU+JQogIGFkZF90cmFjZSh4ID0gfkpQX1NhbGVzX1N1bSwgbmFtZSA9ICJKYXBhbiIpICU+JQogIGFkZF90cmFjZSh4ID0gfk90aGVyX1NhbGVzX1N1bSwgbmFtZSA9ICJPdGhlciIpICU+JQogIGxheW91dCgKICAgIHRpdGxlID0gIlZpZGVvIEdhbWUgU2FsZXMgYnkgR2VucmUiLAogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIChtaWxsaW9uKSIpLAogICAgYmFybW9kZSA9ICJncm91cCIKICApCgpmaWcKYGBgCkhpZXIgc2llaHQgbWFuIGd1dCwgZGFzcyBKYXBhbiBpbSBWZXJnbGVpY2ggenUgZGVuIGFuZGVyZW4gTcOkcmt0ZW4gdmllbCBtZWhyIFJvbGUtUGxheSB1bmQgU3RyYXRlZ2llIFNwaWVsZSB2ZXJrYXVmdC4gRGFmw7xyIHdlcmRlbiBpbiBKYXBhbiBhYmVyIHZpZWwgd2VuaWdlciBTaG9vdGVyIHVuZCBBY3Rpb24gU3BpZWxlIGFscyBkZW4gYW5kZXJlbiBSZWdpb25lbiB2ZXJrYXVmdC4gCgpgYGB7cn0KZGZfeWVhciA8LSBkZl9jbGVhbiAlPiUKICBncm91cF9ieShZZWFyKSAlPiUKICBzdW1tYXJpemUoCiAgICBOQV9TYWxlc19TdW0gPSBzdW0oTkFfU2FsZXMpLAogICAgRVVfU2FsZXNfU3VtID0gc3VtKEVVX1NhbGVzKSwgCiAgICBKUF9TYWxlc19TdW0gPSBzdW0oSlBfU2FsZXMpLAogICAgT3RoZXJfU2FsZXNfU3VtID0gc3VtKE90aGVyX1NhbGVzKSwKICAgIEdsb2JhbF9TYWxlc19TdW0gPSBzdW0oR2xvYmFsX1NhbGVzKQogICkKCmZpZyA8LSBwbG90X2x5KAogIGRmX3llYXIsIHkgPSB+TkFfU2FsZXNfU3VtLCB4ID0gflllYXIsIHR5cGUgPSAiYmFyIiwgbmFtZSA9ICJOb3J0aCBBbWVyaWNhIiwgd2lkdGggPSA5MDAsIGhlaWdodCA9IDUwMCkgJT4lIAogIGFkZF90cmFjZSh5ID0gfkVVX1NhbGVzX1N1bSwgbmFtZSA9ICJFdXJvcGUiKSAlPiUKICBhZGRfdHJhY2UoeSA9IH5KUF9TYWxlc19TdW0sIG5hbWUgPSAiSmFwYW4iKSAlPiUKICBhZGRfdHJhY2UoeSA9IH5PdGhlcl9TYWxlc19TdW0sIG5hbWUgPSAiT3RoZXIiKSAlPiUKICBsYXlvdXQoCiAgICB0aXRsZSA9ICJWaWRlbyBHYW1lIGZyb20gU2FsZXMgYnkgWWVhciIsCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiWWVhciIpLAogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIChtaWxsaW9uKSIpLAogICAgYmFybW9kZSA9ICJzdGFjayIKICApCgpmaWcKYGBgCsOcYmVycmFzY2hlbmRlcndlaXNlLCBzaWVodCBtYW4gaW4gZGllc2VtIERpYWdyYW1tLCBkYXNzIGRpZSBWaWRlb3NwaWVsIFZlcmvDpHVmZSB6d2lzY2hlbiBkZW4gSmFocmVuIDIwMDUgdW5kIDIwMTIgZ2Vib29tdCBoYWJlbi4gRGFuYWNoIGlzdCBkaWUgS3VydmUgd2llZGVyIGFiZ2VmbGFjaHQuIERpZXMga8O2bm50ZSB1bnRlciBhbmRlcmVtIGRhcmFuIGxpZWdlbiwgZGFzcyBpbiBkaWVzZXIgWmVpdCB2aWVsZSBuZXVlIEtvbnNvbGVuIGF1ZiBkZW4gTWFya3QgZ2Vrb21tZW4gc2luZCwgd2VsY2hlIGdyb3NzZSBOZXVlcnVuZ2VuIG1pdCBzaWNoIGJyYWNodGVuLiBEaWUgUGxheXN0YXRpb24gMywgZGllIFhib3ggMzYwIHVuZCBkaWUgTmludGVuZG8gV2lpIHNpbmQgYWxsZSBpbiAyMDA1LTIwMDYgYXVmIGRlbiBNYXJrdCBnZWtvbW1lbiB1bmQgaGFiZW4gZGVuIFNwaWVsZW1hcmt0IGFuc2NoZWluZW5kIHN0YXJrIGJlZWluZmx1c3N0LiAKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBvZiBzYWxlcyBvZiBlYWNoIGdlbnJlIGZyb20gZWFjaCByZWdpb24KZGZfc2FsZXNfYXZnIDwtIGRmX2NsZWFuICU+JQogIGdyb3VwX2J5KEdlbnJlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBFVV9TYWxlc19BdmcgPSBtZWFuKEVVX1NhbGVzKSwKICAgIE5BX1NhbGVzX0F2ZyA9IG1lYW4oTkFfU2FsZXMpLAogICAgSlBfU2FsZXNfQXZnID0gbWVhbihKUF9TYWxlcyksCiAgICBPdGhlcl9TYWxlc19BdmcgPSBtZWFuKE90aGVyX1NhbGVzKSwKICAgIEdsb2JhbF9TYWxlc19BdmcgPSBtZWFuKEdsb2JhbF9TYWxlcykpCgpkZl9zYWxlc19hdmcKYGBgCkhpZXIgc2VoZW4gd2lyIGRpZSBkdXJjaHNjaG5pdHRsaWNoZSBBbnpoYWwgdm9uIFZlcmvDpHVmZSBwcm8gR2VucmUuCgojIyBSZWdyZXNzaW9uc21vZGVsbGUKCkRhIHdpciB1bnNlcmUgRGF0ZW4gamV0enQgYmVzc2VyIHZlcnN0ZWhlbiwga8O2bm5lbiB3aXIgbWl0IGRlbiBSZWdyZXNzaW9uc21vZGVsbGVuIHVuZCBtaXQgZGVyIEJlYW50d29ydHVuZyB1bnNlcmVyIEZyYWdlc3RlbGx1bmcgYmVnaW5uZW4uIEFsbGVyZGluZ3MgbXVzc3RlbiB3aXIgZmVzdHN0ZWxsZW4sIGRhc3MgdW5zZXJlIEZyYWdlc3RlbGx1bmcgdW5nZXNjaGlja3Qgd2FyIHVuZCB3aXIgZGllc2UgbmljaHQgYXVzcmVpY2hlbmQgYmVhbnR3b3J0ZW4ga8O2bm5lbi4gRGFoZXIgaGFiZW4gd2lyIHVucyBmb2xnZW5kZSBBbHRlcm5hdGl2LUZyYWdlc3RlbGx1bmcgw7xiZXJsZWd0OgoKIyMjIEthbm4gbWFuIGFuaGFuZCBkZXIgbm9yZGFtZXJpa2FuaXNjaGVuIFZlcmvDpHVmZSB2b3JhdXNzYWdlbiwgd2llIHNpY2ggZWluIEdlbnJlIGltIGV1cm9ww6Rpc2NoZW4gTWFya3QgdmVya2F1ZmVuIHdpcmQ/CgpEaWVzZSBGcmFnZSBrw7ZubnRlIGVpbmVyIEZpcm1hIGRhYmVpIGhlbGZlbiB6dSBlbnRzY2hlaWRlbiwgd2lldmllbCBXZXJiZWJ1ZGdldCBkaWVzZSBpbiBFdXJvcGEgYXVzZ2ViZW4gc29sbCwgbmFjaGRlbSBlaW4gU3BpZWwgaW4gQW1lcmlrYSBiZXJlaXRzIGF1ZiBkZW4gTWFya3QgZ2Vrb21tZW4gaXN0LiAKCiMjIyBEYXRlbiBvcHRpbWllcmVuCgpJbiB1bnNlcmVtIERhdGVuc2F0eiBnaWJ0IGVzIGVpbmlnZSBBdXNyZWlzc2VyLiBadW0gQmVpc3BpZWwgZ2lidCBlcyBTcGllbGUsIHdlbGNoZSBudXIgaW4gZWluemVsbmVuIFJlZ2lvbmVuIGF1ZiBkZW4gTWFya3QgZ2Vrb21tZW4gc2luZCB1bmQgZGllIGluIGFuZGVyZW4gTMOkbmRlcm4gZ2FybmljaHQgdmVya2F1ZnQgd3VyZGVuLiBEaWVzZSBUYXRzYWNoZSB3w7xyZGUgYmVpbSBFcnN0ZWxsZW4gZGVyIE1vZGVsbGUgenUgZ3Jvc3NlbiBBYndlaWNodW5nZW4gdW5kIFVuZ2VuYXVpZ2tlaXRlbiBmw7xocmVuLiBEYWhlciBiZXLDvGNrc2ljaHRpZ2VuIHdpciBudXIgZGllIEdhbWVzLCB3ZWxjaGUgw7xiZXIgMSBNaW8uIFZlcmvDpHVmZSBoYWJlbi4KClVudGVuIGhhYmVuIHdpciBkaWUgR2VucmVzIG1pdCBkZW4gYmVzdGVuIEZpdHMgZ2Vub21tZW4sIGRhIGJlaSBkZW4gYW5kZXJlbiBTcGllbGdlbnJlcyBkZXIgci1zcXVhcmVkIFdlcnQgdW50ZXIgNTAlIGxhZy4gRGFzIGJlZGV1dGV0LCBkYXNzIGVzIGRvcnQga2VpbmVuIFp1c2FtbWVuaGFuZyBnaWJ0LCB3ZXNoYWxiIHdpciBkaWVzZSBlYmVuZmFsbHMgbmljaHQgYmVhY2h0ZW4gd2VyZGVuLgoKCldpciBlcnN0ZWxsZW4gZGFzIERhdGFmcmFtZSBtaXQgYWxsZW4gUmFjaW5nIEdhbWVzOgpgYGB7cn0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBSYWNpbmcgZ2FtZXMKZGZfcmFjaW5nIDwtIGRmX2NsZWFuICU+JQogIGZpbHRlcigKICAgIEdlbnJlID09ICJSYWNpbmciLAogICAgTkFfU2FsZXMgPiAxLjAwLAogICAgRVVfU2FsZXMgPiAxLjAwCiAgKQoKI2NyZWF0ZSBzY2F0dGVycGxvdApnZ3Bsb3QoZGZfcmFjaW5nLCBhZXMoeD1FVV9TYWxlcywgeT1OQV9TYWxlcykpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsKICBsYWJzKHRpdGxlID0gIlJhY2luZyBHYW1lIFNhbGVzIE92ZXJ2aWV3IikKYGBgIApgYGB7cn0KIyBDcmVhdGUgbGluZWFyIG1vZGVsCm1kbF9yYWNpbmcgPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3JhY2luZykKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfcmFjaW5nICU+JQogIGdsYW5jZSgpICU+JQogIHB1bGwoci5zcXVhcmVkKQoKIyBQcmVkaWN0IEVVIFNhbGVzIGZvciBhIFJhY2luZyBHYW1lIGJhc2VkIG9uIE5BIFNhbGVzCnByZWRpY3RfcmFjaW5nIDwtIHRpYmJsZShOQV9TYWxlcyA9IDUpCnByZWRpY3QobWRsX3JhY2luZywgcHJlZGljdF9yYWNpbmcpCmBgYApXZW5uIGVpbiBSYWNpbmcgR2FtZSBpbiBOb3JkIEFtZXJpa2EgNSBNaWxsaW9uZW4gVmVya8OkdWZlIGF1ZndlaXN0LCBsaWVndCBkaWUgVmVya2F1ZnMtVm9yaGVyc2FnZSBmw7xyIEV1cm9wYSBiZWkgcnVuZCAzLjkgTWlsbGlvbmVuLiBJbiBBbWVyaWthIHNpbmQgZGllc2UgU3BpZWxlIGFsc28gYmVsaWVidGVyIGFscyBiZWkgdW5zIGluIEV1cm9wYS4KCmBgYHtyfQojIENyZWF0ZSBEYXRhRnJhbWUgb25seSB3aXRoIFJvbGUtUGxheWluZyBnYW1lcwpkZl9yb2xlIDwtIGRmX2NsZWFuICU+JQogIGZpbHRlcigKICAgIEdlbnJlID09ICJSb2xlLVBsYXlpbmciLAogICAgTkFfU2FsZXMgPiAxLjAwLAogICAgRVVfU2FsZXMgPiAxLjAwCiAgKQoKI2NyZWF0ZSBzY2F0dGVycGxvdApnZ3Bsb3QoZGZfcm9sZSwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJSb2xlLVBsYXlpbmcgR2FtZSBTYWxlcyBPdmVydmlldyIpCmBgYApgYGB7cn0KIyBDcmVhdGUgbGluZWFyIG1vZGVsCm1kbF9ycCA8LSBsbShFVV9TYWxlcyB+IE5BX1NhbGVzLCBkYXRhID0gZGZfcm9sZSkKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfcnAgJT4lCiAgZ2xhbmNlKCkgJT4lCiAgcHVsbChyLnNxdWFyZWQpCgojIFByZWRpY3QgRVUgU2FsZXMgZm9yIGEgUm9sZS1QbGF5aW5nIEdhbWUgYmFzZWQgb24gTkEgU2FsZXMKcHJlZGljdF9ycCA8LSB0aWJibGUoTkFfU2FsZXMgPSA1KQpwcmVkaWN0KG1kbF9ycCwgcHJlZGljdF9ycCkKYGBgCmBgYHtyfQojIENyZWF0ZSBEYXRhRnJhbWUgb25seSB3aXRoIFNob290ZXIgZ2FtZXMKZGZfc2hvb3RlciA8LSBkZl9jbGVhbiAlPiUKICBmaWx0ZXIoCiAgICBHZW5yZSA9PSAiU2hvb3RlciIsCiAgICBOQV9TYWxlcyA+IDAuMDAsCiAgICBFVV9TYWxlcyA+IDAuMDAKICApCgojIENyZWF0ZSBEYXRhRnJhbWUgb25seSB3aXRoIFNob290ZXIgZ2FtZXMgd2l0aG91dCBEdWNrIEh1bnQKZGZfc2hvb3Rlcl9ub19kdWNraHVudCA8LSBkZl9jbGVhbiAlPiUKICBmaWx0ZXIoCiAgICBHZW5yZSA9PSAiU2hvb3RlciIsCiAgICBOQV9TYWxlcyA+IDAuMDAsCiAgICBOYW1lICE9ICJEdWNrIEh1bnQiLAogICAgRVVfU2FsZXMgPiAwLjAwCiAgKQoKIyBDcmVhdGUgc3VicGxvdHMKcDEgPC0gZ2dwbG90KGRmX3Nob290ZXIsIGFlcyh4PUVVX1NhbGVzLCB5PU5BX1NhbGVzKSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkgKwogIGxhYnModGl0bGUgPSAiU2hvb3RlciBHYW1lIFNhbGVzIikKCnAyIDwtIGdncGxvdChkZl9zaG9vdGVyX25vX2R1Y2todW50LCBhZXMoeD1FVV9TYWxlcywgeT1OQV9TYWxlcykpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsKICBsYWJzKHRpdGxlID0gIlNob290ZXIgR2FtZSBTYWxlcyBubyBEdWNrIEh1bnQiKQoKIyBQbG90IGJvdGggcGxvdHMgc2lkZSBieSBzaWRlCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2wgPSAyKSAgCmBgYApEaWVzIGlzdCBlaW4gVmVyZ2xlaWNoIGRlciBub3JkYW1lcmlrYW5pc2NoZW4gdW5kIGRlciBldXJvcMOkaXNjaGVuIFZlcmvDpHVmZW4gdm9uIFNob290ZXItU3BpZWxlbi4gQXVmIGRlbSBsaW5rZW4gUGxvdCBpc3QgZWluIGdhbnogZ2V3YWx0aWdlciBBdXNyZWlzc2VyIHp1IHNlaGVuLCBnYW56IG9iZW4gbGlua3MgaW0gQmlsZC4gRGFzIGlzdCBkYXMgU3BpZWwgIkR1Y2sgSHVudCIuIFdpciBoYWJlbiBtZWhyIMO8YmVyIGRpZXNlcyBTcGllbCByZWNoZXJjaGllcnQgdW5kIGhlcmF1c2dlZnVuZGVuLCBkYXNzIGRpZXNlcyBTcGllbCB6dW0gUmVsZWFzZSBkZXMgTmludGVuZG8gRW50ZXJ0YWlubWVudCBTeXN0ZW0ga3VyeiBORVMgaGVyYXVzZ2Vrb21tZW4gaXN0LiBCZWltIEthdWYgZWluZXIgTkVTIGdhYiBlcyBkYXMgU3BpZWwgbWVpc3RlbnMgZGF6dS4gWnUgZGVtIFplaXRwdW5rdCwga2FtIGRpZSBORVMgYWJlciBlcnN0IGluIEFtZXJpa2EgYXVmIGRlbiBNYXJrdCB1bmQgbm9jaCBuaWNodCBpbiBFdXJvcGEuIERhaGVyIHNpZWh0IG1hbiwgZGFzcyBkaWUgVmVya8OkdWZlIGluIEFtZXJpa2EgZmFzdCBiZWkgMzAgTWlvLiBsYWcgdW5kIGluIGV1cm9wYSBzb2dhciB3ZW5pZ2VyIGFscyAxIE1pby4gRWluaGVpdGVuLiBJbSBQbG90IHJlY2h0cywgaGFiZW4gd2lyIGRpZXNlbiBBdXNyZWlzZXIgZW50ZmVybnQgdW5kIHdpciBzZWhlbiBkaXJla3QgZWluIHZpZWwgZ2VuYXVlcmVzIEJpbGQuIAoKYGBge3J9CiMgQ3JlYXRlIGxpbmVhciBtb2RlbAptZGxfc2hvb3RlciA8LSBsbShFVV9TYWxlcyB+IE5BX1NhbGVzLCBkYXRhID0gZGZfc2hvb3RlcikKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfc2hvb3RlciAlPiUKICBnbGFuY2UoKSAlPiUKICBwdWxsKHIuc3F1YXJlZCkKYGBgCmBgYHtyfQojIENyZWF0ZSBsaW5lYXIgbW9kZWwKbWRsX3Nob290ZXJfbm9fZHVja2h1bnQgPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3Nob290ZXJfbm9fZHVja2h1bnQpCgojIEV4dHJhY3QgbW9kZWwgc2NvcmUKbWRsX3Nob290ZXJfbm9fZHVja2h1bnQgJT4lCiAgZ2xhbmNlKCkgJT4lCiAgcHVsbChyLnNxdWFyZWQpCmBgYApXaWUgd2lyIGhpZXIgYW5oYW5kIGRlciBSLVNxdWFyZWQgc2VoZW4sIGZpdHRlZCBkYXMgTW9kZWxsIHZpZWwgYmVzc2VyIG9obmUgRHVjayBIdW50LiAKCkFiZXIgZGEgZGFzIE1vZGVsbCBhdWNoIG9obmUgRHVjayBIdW50IG5pY2h0IHNlaHIgZ3V0IGlzdCwgd2VyZGVuIHdpciBtaXQgZGllc2VtIEdlbnJlIGtlaW5lIFZvcmhlcnNhZ2VuIGR1cmNoZsO8aHJlbi4gRGllc2Ugd8OkaHJlbiB6dSB1bmdlbmF1IHVuZCB3aXIga8O2bm50ZW4gbmljaHQgZGFoaW50ZXIgc3RlaGVuLgoKYGBge3J9CiMgQ3JlYXRlIERhdGFGcmFtZSBvbmx5IHdpdGggU2ltdWxhdGlvbiBnYW1lcwpkZl9zaW0gPC0gZGZfY2xlYW4gJT4lCiAgZmlsdGVyKAogICAgR2VucmUgPT0gIlNpbXVsYXRpb24iLAogICAgTkFfU2FsZXMgPiAxLjAwLAogICAgRVVfU2FsZXMgPiAxLjAwCikKCmdncGxvdChkZl9zaW0sIGFlcyh4PUVVX1NhbGVzLCB5PU5BX1NhbGVzKSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkgKwogIGxhYnModGl0bGUgPSAiU2ltdWxhdGlvbiBHYW1lIFNhbGVzIE92ZXJ2aWV3IikKYGBgCkF1ZiBkaWVzZW0gUGxvdCBnaWJ0IGVzIHdpZWRlciBlaW5lbiBQdW5rdCwgZGVyIHdlaXQgd2VnIHZvbiBkZW4gYW5kZXJlbiBpc3QuIERpZXMgaXN0IGplZG9jaCBpbiBkaWVzZW0gRmFsbCBrZWluIEF1c3JlaXNlciwgZGEgaGllciBkaWUgVmVya8OkdWZlIHZvbiBOb3JkYW1lcmlrYSB1bmQgRXVyb3BhIHNlaHIgw6RobmxpY2ggc2luZC4gQmVpIGRpZXNlbSBleHRyZW0gYmVsaWVidGVuIFNwaWVsIGhhbmRlbHQgZXMgc2ljaCDDvGJyaWdlbnMgdW0gZGVuIEh1bmRlc2ltdWxhdG9yICJOaW50ZW5kb2dzIi4gOy0pCgpgYGB7cn0KIyBDcmVhdGUgbGluZWFyIG1vZGVsCm1kbF9zaW0gPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3NpbSkKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfc2ltICU+JQogIGdsYW5jZSgpICU+JQogIHB1bGwoci5zcXVhcmVkKQoKIyBQcmVkaWN0IEVVIFNhbGVzIGZvciBhIFNpbXVsYXRpb24gR2FtZSBiYXNlZCBvbiBOQSBTYWxlcwpwcmVkaWN0X3NpbSA8LSB0aWJibGUoTkFfU2FsZXMgPSA1KQpwcmVkaWN0KG1kbF9zaW0sIHByZWRpY3Rfc2ltKQpgYGAKV2VubiBlaW4gU2ltdWxhdGlvbiBHYW1lIGluIE5BIDUgTWlsbGlvbiBWZXJrw6R1ZmUgYXVmd2Vpc3QsIGxpZWd0IGRpZSBWb3JoZXJzYWdlIGbDvHIgRVUgYmVpIDUuNCBNaWxsaW9uZW4uCgpgYGB7cn0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBTcG9ydCBnYW1lcwpkZl9zcG9ydCA8LSBkZl9jbGVhbiAlPiUKICBmaWx0ZXIoCiAgICBHZW5yZSA9PSAiU3BvcnRzIiwKICAgIE5BX1NhbGVzID4gMS4wMCwKICAgIEVVX1NhbGVzID4gMS4wMAogICkKCmdncGxvdChkZl9zcG9ydCwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJTcG9ydCBHYW1lIFNhbGVzIE92ZXJ2aWV3IikKYGBgCkhpZXIgaGFiZW4gd2lyIHdpZWRlciBlaW4gU3BpZWwsIHdlbGNoZXMgc2ljaCB2b24gZGVuIE1hc3NlbiBhYmhlYnQsIGFiZXIgaW4gQW1lcmlrYSBzb3dpZSBpbiBFdXJvcGEgZXh0cmVtIGJlbGllYnQgd2FyLiBFcyBoYW5kZWx0IHNpY2ggbmF0w7xybGljaCB1bSBkYXMgS3VsdHNwaWVsICJXaWkgU3BvcnRzIi4gRGllc2VzIHd1cmRlIGVpbmVtIGF1Y2ggbWVpc3RlbnMgYmVpbSBLYXVmIGVpbmVyIE5pbnRlbmRvIFdpaSBLb25zb2xlIGRhenUgZ2VzY2hlbmt0LiAKCmBgYHtyfQojIENyZWF0ZSBsaW5lYXIgbW9kZWwKbWRsX3Nwb3J0IDwtIGxtKEVVX1NhbGVzIH4gTkFfU2FsZXMsIGRhdGEgPSBkZl9zcG9ydCkKCiMgRXh0cmFjdCBtb2RlbCBzY29yZQptZGxfc3BvcnQgJT4lCiAgZ2xhbmNlKCkgJT4lCiAgcHVsbChyLnNxdWFyZWQpCgojIFByZWRpY3QgRVUgU2FsZXMgZm9yIGEgU3BvcnQgR2FtZSBiYXNlZCBvbiBOQSBTYWxlcwpwcmVkaWN0X3Nwb3J0IDwtIHRpYmJsZShOQV9TYWxlcyA9IDUpCnByZWRpY3QobWRsX3Nwb3J0LCBwcmVkaWN0X3Nwb3J0KQpgYGAKV2VubiBlaW4gU3BvcnQgR2FtZSBpbiBOQSA1IE1pbGxpb24gVmVya8OkdWZlIGF1ZndlaXN0LCBsaWVndCBkaWUgVm9yaGVyc2FnZSBmw7xyIEVVIGJlaSA0IE1pbGxpb25lbi4KCgojIyMgUmVzaWR1ZW5hbmFseXNlICh6dW0gYmV1cnRlaWxlbiBvYiBkYXMgTW9kZWxsIGd1dCBpc3QpCkRpZSBSZXNpZHVlbiBzb2xsdGVuIGZvbGdlbmRlIFB1bmt0ZSBlcmbDvGxsZW46CgotIFJlc2lkdWVuIGhhYmVuIGRlbiBlcndhcnR1bmdzd2VydCB2b24gMAotIFJlc2lkdWVuIHNpbmQgdm9uZWluYW5kZXIgdW5hYmjDpG5naWcKLSBSZXNpZHVlbiBzaW5kIG5vcm1hbHZlcnRlaWx0CgpEaWVzIG3DvHNzZW4gd2lyIG51biBub2NoIHByw7xmZW4sIHVtIHp1IGJlc3RpbW1lbiwgb2IgdW5zZXJlIFZvcmhlcnNhZ2VuIHZlcmzDpHNzbGljaCBzaW5kLiAKCmBgYHtyfQojIENyZWF0ZSBSZXNpZHVhbCBTY2F0dGVycGxvdAoKZGYgPC0gYXVnbWVudChtZGxfcmFjaW5nKQoKIyBwbG90IHJlc2lkdWFscwpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUmFjaW5nIEdlbnJlIikgKwogIHhsYWIoIiIpCmBgYApgYGB7cn0KIyBDcmVhdGUgUmVzaWR1YWwgSGlzdG9ncmFtICh0byBzZWUgaWYgdGhlIGRhdGEgaXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uKQoKZ2dwbG90KGRmX3JhY2luZywgYWVzKHggPSBtZGxfcmFjaW5nJHJlc2lkdWFscykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUmFjaW5nIEdlbnJlIikgKwogIHhsYWIoInJlc2lkdWFscyIpCmBgYApNaXQgSGlsZmUgZGVyIHJvdGVuIExpbmllIHNpZWh0IG1hbiwgZGFzcyBkYXMgSGlzdG9ncmFtbSAgbmljaHQgMTAwJSBub3JtYWx2ZXJ0ZWlsdCBpc3QuIEVzIGtvbW10IGplZG9jaCBzY2hvbiBuYWhlIGFuIGVpZSBOb3JtYWx2ZXJ0ZWlsdW5nIGhlcmFuLCB3ZXNoYWxiIHdpciBkYXMgc28gYWt6ZXB0aWVyZW4ga8O2bm5lbi4KCmBgYHtyfQojIENyZWF0ZSBSZXNpZHVhbCBTY2F0dGVycGxvdAoKZGYgPC0gYXVnbWVudChtZGxfcnApCgpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUm9sZS1QbGF5aW5nIEdlbnJlIikgKwogIHhsYWIoIiIpCmBgYApgYGB7cn0KIyBDcmVhdGUgUmVzaWR1YWwgSGlzdG9ncmFtICh0byBzZWUgaWYgdGhlIGRhdGEgaXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uKQoKZ2dwbG90KGRmX3JvbGUsIGFlcyh4ID0gbWRsX3JwJHJlc2lkdWFscykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUm9sZS1QbGF5aW5nIEdlbnJlIikgKwogIHhsYWIoInJlc2lkdWFscyIpCmBgYApBdWNoIHNpZWh0IG1hbiwgZGFzcyBkYXMgSGlzdG9ncmFtbSAgbmljaHQgMTAwJSBub3JtYWx2ZXJ0ZWlsdCBpc3QuIEVzIGtvbW10IGplZG9jaCBzY2hvbiBuYWhlIGFuIGVpZSBOb3JtYWx2ZXJ0ZWlsdW5nIGhlcmFuLCB3ZXNoYWxiIHdpciBkYXMgc28gYWt6ZXB0aWVyZW4ga8O2bm5lbi4KYGBge3J9CiMgQ3JlYXRlIFJlc2lkdWFsIFNjYXR0ZXJwbG90CgpkZiA8LSBhdWdtZW50KG1kbF9zaW0pCgpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU2ltdWxhdGlvbiBHZW5yZSIpICsKICB4bGFiKCIiKQpgYGAKYGBge3J9CiMgQ3JlYXRlIFJlc2lkdWFsIEhpc3RvZ3JhbSAodG8gc2VlIGlmIHRoZSBkYXRhIGlzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbikKCmdncGxvdChkZl9zaW0sIGFlcyh4ID0gbWRsX3NpbSRyZXNpZHVhbHMpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKwogIGdlb21fZGVuc2l0eShjb2xvciA9ICJSZWQiKSArCiAgZ2d0aXRsZSgiUmVzaWR1YWxzIE1vZGVsIFNpbXVsYXRpb24gR2VucmUiKSArCiAgeGxhYigicmVzaWR1YWxzIikKYGBgCk1pdCBIaWxmZSBkZXIgcm90ZW4gTGluaWUgc2llaHQgbWFuLCBkYXNzIGRhcyBIaXN0b2dyYW1tICBuaWNodCAxMDAlIG5vcm1hbHZlcnRlaWx0IGlzdC4gRXMga29tbXQgamVkb2NoIHNjaG9uIG5haGUgYW4gZWllIE5vcm1hbHZlcnRlaWx1bmcgaGVyYW4sIHdlc2hhbGIgd2lyIGRhcyBzbyBha3plcHRpZXJlbiBrw7ZubmVuLgoKCmBgYHtyfQojIENyZWF0ZSBSZXNpZHVhbCBTY2F0dGVycGxvdAoKZGYgPC0gYXVnbWVudChtZGxfc3BvcnQpCgpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU3BvcnQgR2VucmUiKSArCiAgeGxhYigiIikKYGBgCmBgYHtyfQojIENyZWF0ZSBSZXNpZHVhbCBIaXN0b2dyYW0gKHRvIHNlZSBpZiB0aGUgZGF0YSBpcyBhIG5vcm1hbCBkaXN0cmlidXRpb24pCgpnZ3Bsb3QoZGZfc3BvcnQsIGFlcyh4ID0gbWRsX3Nwb3J0JHJlc2lkdWFscykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsKICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU3BvcnQgR2VucmUiKSArCiAgeGxhYigicmVzaWR1YWxzIikKYGBgCk1pdCBIaWxmZSBkZXIgcm90ZW4gTGluaWUgc2llaHQgbWFuLCBkYXNzIGRhcyBIaXN0b2dyYW1tICBuaWNodCAxMDAlIG5vcm1hbHZlcnRlaWx0IGlzdC4gRXMga29tbXQgamVkb2NoIHNjaG9uIG5haGUgYW4gZWllIE5vcm1hbHZlcnRlaWx1bmcgaGVyYW4sIHdlc2hhbGIgd2lyIGRhcyBzbyBha3plcHRpZXJlbiBrw7ZubmVuLgoKIyMjIFdpZSDDpGhubGljaCBzaW5kIHNpY2ggZGllIFZlcmthdWZzemFobGVuIHZvbiBOb3JkYW1lcmlrYSB1bmQgRXVyb3BhPyBVbmQgd2llIHZlcmhhbHRlbiBzaWNoIGRpZSBqYXBhbmlzY2hlbiBWZXJrYXVmc3phaGxlbiBpbSBWZXJnbGVpY2ggenUgZGVuIHp3ZWkgd2VzdGxpY2hlbiBBYnNhdHptw6Rya3Rlbj8KCmBgYHtyfQojIENvbG9yIHRoZSBTUExPTSBvZiBOQV9TYWxlcywgRVVfU2FsZXMsIGFuZCBKUF9TYWxlcyBieSBuaW50ZW5kbwpkZl9jbGVhbiAlPiUKICBwbG90X2x5KGNvbG9yID0gfkdlbnJlKSAlPiUgCiAgYWRkX3RyYWNlKAogICAgdHlwZSA9ICdzcGxvbScsCiAgICBkaW1lbnNpb25zID0gbGlzdCgKICAgICAgbGlzdChsYWJlbCA9ICdOLiBBbWVyaWNhJywgdmFsdWVzID0gfk5BX1NhbGVzKSwKICAgICAgbGlzdChsYWJlbCA9ICdFdXJvcGUnLCB2YWx1ZXMgPSB+RVVfU2FsZXMpLCAgICAKICAgICAgbGlzdChsYWJlbCA9ICdKYXBhbicsIHZhbHVlcyA9IH5KUF9TYWxlcykgICAgICAgCiAgICApCiAgKQpgYGAKw4R1c3NlcnN0IHNwYW5uZW5kIHp1IHNlaGVuIGlzdCwgZGFzcyBzaWNoIGdyb3NzZSBVbnRlcnNjaGllZGUgc2VoZW4gbGFzc2VuLCB3aWUgc2ljaCBiZXN0aW1tdGUgR2VucmUgdmVya2F1ZnQgaGFiZW4uIERpZSBnZW9ncmFwaGlzY2hlIExhZ2Ugdm9uIGRlbSBBYnNhdHptYXJrdCBpc3QgZGFiZWkgZWhlciB6d2VpdHJhbmdpZy4gTWl0IEFic3RhbmQgYW0gaMOkdWZpZ3N0ZW4gd3VyZGVuIEFjdGlvbiBTcGllbGUgdmVya2F1ZnQuIAoKCiMjIyBWZXJrYXVmZW4gc2ljaCBOaW50ZW5kbyBTcGllbGUgd2VsdHdlaXQgYmVzc2VyIGFscyBTcGllbGUgRWxlY3Ryb25pYyBBcnRzLiAKCkRhZsO8ciBtw7xzc2VuIHdpciB6dWVyc3QgdW5zZXIgRGF0YWZyYW1lIEZpbHRlcm4sIGRhbWl0IHdpciBudXJub2NoIFNwaWVsZSB2b24gTmludGVuZG8gdW5kIEVsZWN0cm9uaWMgQXJ0cyBoYWJlbi4KYGBge3J9CiMgQ3JlYXRlIERhdGFGcmFtZSB3aXRoIG9ubHkgTmludGVuZG8gYW5kIFNvbnkgYXMgUHVibGlzaGVyCnB1Ymxpc2hlcnMgPSBjKCJOaW50ZW5kbyIsICJFbGVjdHJvbmljIEFydHMiKQoKZGZfcHVibGlzaGVyIDwtIGRmX2NsZWFuICU+JQogIGZpbHRlcigKICAgIFB1Ymxpc2hlciAlaW4lIHB1Ymxpc2hlcnMKICAgICkKCmRmX3B1Ymxpc2hlcgpgYGAKYGBge3J9CiMgUmVwbGFjZSBwdWJsaXNoZXIgbmFtZSB3aXRoIDAgYW5kIDEKZGZfcHVibGlzaGVyJFB1Ymxpc2hlcltkZl9wdWJsaXNoZXIkUHVibGlzaGVyID09ICJFbGVjdHJvbmljIEFydHMiXSA8LSAwCmRmX3B1Ymxpc2hlciRQdWJsaXNoZXJbZGZfcHVibGlzaGVyJFB1Ymxpc2hlciA9PSAiTmludGVuZG8iXSA8LSAxCgojIFNhdmUgYXMgaW50CmRmX3B1Ymxpc2hlciRQdWJsaXNoZXIgPC0gYXMubnVtZXJpYyhkZl9wdWJsaXNoZXIkUHVibGlzaGVyKQpgYGAKCmBgYHtyfQojIENyZWF0ZSBsb2dpc3RpYyBtb2RlbAptZGxfcHVibGlzaGVyIDwtIGdsbShQdWJsaXNoZXIgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3B1Ymxpc2hlciwgZmFtaWx5ID0gYmlub21pYWwoKSkKYGBgCgpgYGB7cn0KZ2dwbG90KGRmX3B1Ymxpc2hlciwgYWVzKHg9R2xvYmFsX1NhbGVzLCB5PVB1Ymxpc2hlcikpICsgCiAgZ2VvbV9wb2ludChhbHBoYT0uNSwgY29sb3I9IkJsdWUiKSArCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJnbG0iLCBjb2wgPSAiUmVkIiwgc2U9RkFMU0UsIG1ldGhvZC5hcmdzID0gbGlzdChmYW1pbHk9Ymlub21pYWwpKSArCiAgbGFicygKICAgIHggPSAiU2FsZXMgKG1pbGxpb24pIiwKICAgIHkgPSAiMT1OaW50ZW5kbyAvIDA9RWxlY3Ryb25pYyBBcnRzIiwKICAgIHRpdGxlID0gIlByb2JhYmlsaXR5IHRoYXQgYSBnYW1lIGlzIGZyb20gTmludGVuZG8gYmFzZWQgb24gZ2xvYmFsIHNhbGVzIgogICkKYGBgCldpciBrw7ZubmVuIGhpZXIgZ3V0IHNlaGVuLCBkYXNzIE5pbnRlbmRvIHZpZWwgZXJmb2xncmVpY2hlcmUgU3BpZWxlIHByb2R1emllcnQgaGF0LiBEaWVzIGRldXRldCBhdWNoIGFuLCBkYXNzIE5pbnRlbmRvIGJlbGllYnRlciBpc3QgYWxzIEVsZWN0cm9uaWMgQXJ0cy4gCgoKIyMjIFZlcmthdWZlbiBzaWNoIGluIEphcGFuIE5pbnRlbmRvIFdpaSBTcGllbGUgYmVzc2VyIGFscyBOaW50ZW5kbyBEUyBTcGllbGU/CgpOdW4gZmlsdGVybiB3aXIgenVlcnN0IHVuc2VyZW4gRGF0YWZyYW1lIG5hY2ggV2lpIHVuZCBuYWNoIERTIFNwaWVsZW4uCgpgYGB7cn0KIyBDcmVhdGUgRGF0YUZyYW1lIHdpdGggb25seSBXaWkgYW5kIERTIGFzIFBsYXRmb3JtcwpwbGF0Zm9ybXMgPSBjKCJXaWkiLCAiRFMiKQoKZGZfcGxhdGZvcm0gPC0gZGZfY2xlYW4gJT4lCiAgZmlsdGVyKAogICAgUGxhdGZvcm0gJWluJSBwbGF0Zm9ybXMKICAgICkKCmRmX3BsYXRmb3JtCmBgYApgYGB7cn0KIyBSZXBsYWNlIHBsYXRmb3JtIG5hbWUgd2l0aCAwIGFuZCAxCmRmX3BsYXRmb3JtJFBsYXRmb3JtW2RmX3BsYXRmb3JtJFBsYXRmb3JtID09ICJXaWkiXSA8LSAwCmRmX3BsYXRmb3JtJFBsYXRmb3JtW2RmX3BsYXRmb3JtJFBsYXRmb3JtID09ICJEUyJdIDwtIDEKCiMgU2F2ZSBhcyBpbnQKZGZfcGxhdGZvcm0kUGxhdGZvcm0gPC0gYXMubnVtZXJpYyhkZl9wbGF0Zm9ybSRQbGF0Zm9ybSkKYGBgCgpgYGB7cn0KIyBDcmVhdGUgbG9naXN0aWMgbW9kZWwKbWRsX3BsYXRmb3JtIDwtIGdsbShQbGF0Zm9ybSB+IEpQX1NhbGVzLCBkYXRhID0gZGZfcGxhdGZvcm0sIGZhbWlseSA9IGJpbm9taWFsKCkpCmBgYAoKYGBge3J9CmdncGxvdChkZl9wbGF0Zm9ybSwgYWVzKHg9SlBfU2FsZXMsIHk9UGxhdGZvcm0pKSArIAogIGdlb21fcG9pbnQoYWxwaGE9LjIsIGNvbG9yPSJCbHVlIikgKwogIHN0YXRfc21vb3RoKG1ldGhvZD0iZ2xtIiwgY29sID0gIlJlZCIsIHNlPUZBTFNFLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5PWJpbm9taWFsKSkgKwogIGxhYnMoCiAgICB4ID0gIlNhbGVzIChtaWxsaW9uKSIsCiAgICB5ID0gIjE9V2lpIC8gMD1EUyIsCiAgICB0aXRsZSA9ICJQcm9iYWJpbGl0eSB0aGF0IGEgZ2FtZSBpcyBmcm9tIFdpaSBiYXNlZCBvbiBzYWxlcyBpbiBKYXBhbiIKICApCmBgYApBbmhhbmQgdm9uIGRpZXNlbSBQbG90IGthbm4gbWFuIGVya2VubmVuLCBkYXNzIGVpbiBTcGllbCwgd2VsY2hlcyBzaWNoIMO8YmVyIDIgTWlvLiBtYWwgdmVya2F1ZnQgaGF0LCBlaGVyIMO8YmVyIGRpZSBQbGF0Zm9ybSBXaWkgdmVya2F1ZnQgd3VyZGUuIERhcmF1cyBrw7ZubnRlIG1hbiBkZXV0ZW4sIGRhc3MgZGllIE5pbnRlbmRvIFdpaSBpbiBKYXBhbiBiZWxpZWJ0ZXIgaXN0IGFscyBkZXIgTmludGVuZG8gRFMuCgojIyMgRmF6aXQKQmVpIGRlbiBtZWlzdGVuIEdlbnJlbiBpc3QgZXMgbmljaHQgbcO2Z2xpY2ggZGllIFZlcmvDpHVmZSBpbiBFdXJvcGEgYW5oYW5kIGRlciBWZXJrw6R1ZmUgaW4gTm9yZCBBbWVyaWthIHZvcmhlcnp1c2FnZW4uIFdpciBoYWJlbiBqZWRvY2ggZWluaWdlIEdlbnJlbiBnZWZ1bmRlbiwgYmVpIGRlbmVuIFZvcmhlcnNhZ2VuIG3DtmdsaWNoIGlzdDogCgotIFJhY2luZyAvIFJlbm5zcGllbGUKLSBSb2xlLVBsYXlpbmcgLyBSb2xsZW5zcGllbGUKLSBTaW11bGF0aW9uIC8gU2ltdWxhdGlvbnNzcGllbGUKLSBTcG9ydCAvIFNwb3J0c3BpZWxlCgpTZWhyIGludGVyZXNzYW50IHp1IHNlaGVuIHdhciBkYXMgR2VucmUgU2ltdWxhdGlvbi4gVW5zZXIgTW9kZWxsLCB3ZWxjaGVzIGJlaSA5MiUgR2VuYXVpZ2tlaXQgbGllZ3Qgc2FndCB2b3JhdXMsIGRhc3MgZWluIGJlbGllYmlnZXMgU3BpZWwgaW4gRXVyb3BhIGJlc3NlciB2ZXJrYXVmdCB3aXJkIGFscyBpbiBOb3JkIEFtZXJpa2EuIEJlaSBhbGxlbiBhbmRlcmVuIEdlbnJlbiB2ZXJrYXVmZW4gc2ljaCBkaWUgU3BpZWxlIGluIE5vcmQgQW1lcmlrYSBiZXNzZXIu